/**
 * Copyright 2010 Mark Wyszomierski
 */
package com.joelapenna.foursquared.util;

import android.net.Uri;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.os.Process;
import android.util.Log;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Observable;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * Acts as a per-activity image download manager. This is for testing only.
 * The global resource fetcher's requests live between activities. In most
 * cases we want to stop all requests as soon as an activity is completed.
 * 
 * Create an instance of this fetcher as part of an activity's state holder.
 * Add request urls to it as usual. When the activity is finishing, just
 * call shutdown();
 * 
 * We are limited to a single download thread, and we set its process
 * priority to the lowest possible. Therefore image downloads won't 
 * consume much cpu while the user is interacting with the UI (which will
 * mostly end up being the user scrolling lists).
 * 
 * @date October 12, 2010
 * @author Mark Wyszomierski (markww@gmail.com)
 */
public class NewImageFetcher extends Observable {
    
    private static final String TAG = "NewImageFetcher";
    private static final int MSG = 500;
    
    private Set<String> mRequests;
    private AtomicBoolean mRun;
    private WorkerThread mThread;
    private DiskCache mDiskCache;
    
    
    
    
    public NewImageFetcher(String cacheName) {
        this(new BaseDiskCache("foursquare", cacheName));
    }
    
    public NewImageFetcher(DiskCache cache) {
        mDiskCache = cache;
        mRequests = new LinkedHashSet<String>();
        mRun = new AtomicBoolean(true);
        mThread = new WorkerThread();
    }
    
    public void addRequest(String url) {
        synchronized (mRequests) {
            if (!mRequests.contains(url)) {
                mRequests.add(url);
            }
        }
        mThread.notify();
    }
    
    public void shutdown() {
        mRun.set(false);
        mThread.notify();
        mHandler.removeMessages(MSG);
    }
    
    /**
     * Probably makes sense to call this whenever an activity is finishing, we probably
     * don't care about any of those requests, even if some may be useful in the next
     * activity to be shown.
     */
    public void clearRequests() {
        synchronized (mRequests) {
            mRequests.clear();
        }
    }
    
    private class WorkerThread extends Thread {
        public WorkerThread() {
            Log.e(TAG, "Worker thread started.");
        }
        
        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
            
            while (mRun.get()) {
                List<String> requests = new ArrayList<String>();
                synchronized (mRequests) {
                    if (mRequests.size() > 0) {
                        // Copy over values, get out of guard.
                        for (String it : mRequests) {
                            requests.add(it);
                        }
                    } else {
                        try {
                            mRequests.wait();
                        } catch (InterruptedException ex) {
                        }
                    }
                }
                
                // Service any requests we had.
                for (String it : requests) {
                    //uri, Uri.encode(uri.toString());
                    
                    // Store is being down in a background thread.
                    mDiskCache.store(Uri.encode(it), downloadImage(it));
                    mHandler.sendMessage(mHandler.obtainMessage(MSG, it));
                }
            }
        }
    };
    
    private InputStream downloadImage(String url) {
        try {
            URLConnection conn = new URL(url).openConnection();
            conn.setConnectTimeout(7 * 1000);
            conn.setReadTimeout(7 * 1000);
            conn.connect(); 
            return conn.getInputStream(); 
       } catch (Exception ex) { 
           Log.e(TAG, "Error getting bitmap at url [" + url + "].", ex); 
       }
      
       return null;       
    }
    
    private Handler mHandler = new Handler(new Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            notifyObservers(msg.obj);
            /*
            if (mListView != null) {
                ListAdapter adapter = mListView.getAdapter();
                if (adapter instanceof RemoteResourcesListAdapter) {
                    String url = (String)msg.obj;
                    for (int i = 0, m = mListView.getChildCount(); i < m; i++) {
                        View view = mListView.getChildAt(i);
                        ((RemoteResourcesListAdapter)adapter).onResourcesDownloaded(view, url);
                    }
                }
            }
            */
            return false;
        }
    });
}